Skip to content

Conversation

@LegendaryFire
Copy link
Contributor

I was having issues with the Surface Laptop Studio 2 touch pad, where multi-touch gestures were nearly impossible to make work when your fingers are close together. I came up with the idea that one could find local maxima locations, and dim the neighboring pixels resulting in a more reliable way to use multi-touch gestures where fingers are close. Without code changes, the only way I was able to get somewhat decent support was to increase the neutral value offset by about 60 to 70.

This in turn introduced a ton of jitter, and required the activation and deactivation thresholds to be set extremely low when using the cursor. From there, I made an implementation which would start with a high neutral, then back it off if only one touch was detected. This helped, but was still pretty inconsistent and faced the same issues as just having a high neutral offset.

I've done a fair bit of testing with the touch pad with the pixel suppression, and had multiple people trying it out on my laptop. With the attached Surface Laptop Studio 2 configuration, it seems I am able to get a pretty much identical touch pad experience to when running in Windows, with no hiccups.

Perhaps there might be a better way to do this, but this seems to have done the trick for me - figured it would be worth sharing.

@LegendaryFire LegendaryFire marked this pull request as ready for review December 27, 2025 01:32
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a neighboring pixel suppression feature to improve multi-touch gesture support on devices where fingers are positioned close together, particularly targeting the Surface Laptop Studio 2 touchpad. The implementation finds local maxima in the heatmap and darkens surrounding pixels to prevent cluster spanning from incorrectly merging nearby touch contacts, while preserving accurate maxima positions and intensity values for Gaussian fitting.

Key changes:

  • Adds a new suppression algorithm that darkens pixels around detected maxima within a configurable radius by a configurable factor
  • Implements dual-image approach: original blurred image for maxima detection and Gaussian fitting, suppressed image for cluster spanning
  • Adds configuration options PeakSuppressionRadius and PeakSuppressionFactor to control the suppression behavior

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/contacts/detection/algorithms/suppression.hpp New algorithm implementing neighbor pixel suppression with euclidean radius-based darkening and maxima preservation
src/contacts/detection/detector.hpp Integrates suppression into detection pipeline by adding suppressed image buffer and conditional application during cluster spanning
src/contacts/detection/config.hpp Adds configuration fields for suppression radius and factor with documentation
src/core/generic/config.hpp Declares suppression config fields and passes them to detection config
src/core/linux/config-loader.hpp Loads suppression parameters from configuration files
etc/iptsd.conf Documents the new PeakSuppressionRadius and PeakSuppressionFactor configuration options
etc/presets/surface-laptop-studio-2.conf Provides device-specific configuration for Surface Laptop Studio 2 with suppression enabled

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 15 to 30
/*
* Darken pixels around each maxima by a constant factor, while leaving maxima pixels unchanged.
*
* This is intended to "sharpen" peaks / deepen valleys between peaks so that cluster spanning
* is less likely to connect neighboring contacts.
*
* Behavior:
* - out is initialized as a copy of in
* - for each maxima, all pixels within euclidean radius (excluding the center pixel) are reduced:
* out(y,x) = min(out(y,x), in(y,x) * factor)
* - maxima pixels are restored to their original values (exactly unchanged)
*
* Notes:
* - If neighborhoods overlap, the result is still "at most factor" darkening (no stacking),
* because we always compare against (in * factor), not (out * factor).
*/
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function documentation uses a C-style comment (/) instead of the Doxygen-style comment (/!) that is consistently used throughout the codebase for documenting functions. For consistency with other algorithm files like maximas.hpp, cluster.hpp, neutral.hpp, and ellipse.hpp, this should use the Doxygen comment style.

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot propose a commit/change to fix that.

qzed and others added 3 commits December 29, 2025 23:24
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Member

@qzed qzed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a good idea, thanks!

@qzed qzed merged commit c487d1a into linux-surface:master Dec 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants